import os.path
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

import click
import json
from collections import Counter
from operator import itemgetter

from dp_analysis.analysis.base import RecommendBase
from dp_analysis.db.connection import Mysql
from dp_analysis.utils.data_search import search_region
from dp_analysis.utils.decorate import decorate


class PriceRecommend(RecommendBase):
    """
    为新成立餐厅提供价格参考
    """

    def __init__(self):
        super().__init__()

        self.base_sql = """
                        SELECT
                            average_price,
                            lat,
                            lng
                        FROM
                            shop_info 
                        WHERE
                            region = {!r}
                            AND average_price != 0
                            AND lat != ''
                            AND lng != ''
                        """

    @decorate(obj_name="PriceRecommend")
    def analyse(self, db, region, detail_address, cuisine, ratio):
        location_v1 = self.calc_lat_lng(detail_address)
        if not location_v1:
            raise Exception("输入详细地址无法获取地理坐标系，请重新输入...")

        add_sql = ';' if cuisine == "all" else f" AND category IN ({cuisine});"
        sql = self.base_sql.format(region) + add_sql

        price_list = list()
        for (average_price, lat, lng) in self.search_data(db, sql):
            distance = self.calc_distance(location_v1, [float(lat), float(lng)])
            if distance <= ratio:
                price_list.append(int(average_price))

        # print(f"选定区域所有餐厅价格分布如下：\n"
        #       f"{json.dumps({k:v for k, v in Counter(price_list).items()}, indent=2, ensure_ascii=False)}")

        mode, shop_number = self.calc_mode(price_list)
        _min, _max = self.calc_extreme_value(price_list)

        print(f"该系统针对于所选地区提供的价格参考如下：\n"
              f"\t该区域有 {self.output(len(price_list))} 家餐厅...\n"
              f"\t该区域餐厅最低价格为 {self.output(_min)} 元，最高价格为 {self.output(_max)} 元...\n"
              f"\t平均价格为 {self.output(self.calc_average(price_list))} 元左右...\n"
              f"\t大多数店铺的价格为 {self.output(mode)} 元左右，共有 {self.output(shop_number)} 家餐厅...\n"
              f"\t店铺中位数价格为 {self.output(self.calc_median(price_list))} 元左右...")

    @staticmethod
    def search_data(db, sql):
        with Mysql("dp", db) as mysql:
            connection = mysql.engine.connect()
            for data in connection.execute(sql).fetchall():
                yield data

    @staticmethod
    def calc_average(price_list):
        """
        计算平均数
        """
        return sum(price_list) // len(price_list)

    @staticmethod
    def calc_median(price_list):
        """
        计算中位数
        """
        price_list = sorted(price_list)
        mid = len(price_list) // 2
        return (price_list[mid] + price_list[~mid]) // 2

    @staticmethod
    def calc_extreme_value(price_list):
        """
        计算最大值和最小值
        """
        price_list.sort()
        return price_list[0], price_list[-1]

    @staticmethod
    def calc_mode(price_list):
        """
        计算众数
        """
        price_list = sorted(Counter(price_list).items(), key=itemgetter(1), reverse=True)
        max_price = price_list[0][0]
        data_str = ';'.join(map(lambda m: str(m[0]), filter(lambda d: max_price == d[0], price_list)))
        return data_str, price_list[0][1]


@click.command()
@click.option("--db", default=0, help="请选择使用的数据库")
@click.option("--region", prompt="请输入地区（12大地区）", help="无默认值，12大地区通过helpers.py获取")
@click.option("-d", "--detail_address", prompt="请输入所在区域详细地址", help="详细地址，用于获取经纬度")
@click.option("--cuisine", default="all", help="默认所有菜系，各地区所有菜系通过helpers.py获取\n若指定多个以逗号分割")
@click.option("-r", "--ratio", prompt="请输入参考半径（单位：KM）", help="选取参考区域所有的店铺")
def main(db, region, detail_address, cuisine, ratio):
    if region not in search_region(db):
        raise Exception(f"输入地区{region}不在可选范围内...")

    if not detail_address.startswith(region):
        raise Exception(f"详细地址以所在地区为开头来指定...")

    if cuisine != "all":
        cuisine = ",".join([f"{cui.strip()!r}" for cui in cuisine.split(",")])

    price_recommend = PriceRecommend()
    price_recommend.analyse(db, region, detail_address, cuisine, float(ratio))


if __name__ == '__main__':
    # 运行参考如下：
    # python price_recommend.py --db 0 --region 北京 -d 北京市海淀区中关村中钢国际广场 -r 8
    main()
